using System;
using System.Reflection;
using System.Text;
using PropertyExpression.Common;

namespace PropertyExpression.PresentationCode.CodeGenerator
{
    internal class ElementExtensionsGenerator : IExtensionsGenerator
    {
        private readonly Type type;

        public ElementExtensionsGenerator(Type type)
        {
            this.type = type;
        }

        public IOptionalResult<IOverloadedAndPrefixMethodsBuilder> BuildAttachedProperty(
            string propertyName, StringBuilder builder, MethodInfo methodInfo,
            Type parameterType, Type attachedMethodTargetType, FieldInfo fieldInfo)
        {
            return Build(builder, propertyName, parameterType, fieldInfo);
        }

        public IOptionalResult<IOverloadedAndPrefixMethodsBuilder> BuildSimpleProperty(
            string propertyName, StringBuilder builder, FieldInfo fieldInfo, Type parameterType)
        {
            return Build(builder, propertyName, parameterType, fieldInfo);
        }

        private IOptionalResult<IOverloadedAndPrefixMethodsBuilder> Build(StringBuilder builder, string propertyName, Type parameterType, FieldInfo fieldInfo)
        {
            builder.AppendFormat(
                @"        public static IElement<{2}> {0}(this IElement<{2}> it, {1} value)
        {{
            it.SetValue({2}.{3}, value);
            return it;
        }}
",
                propertyName,
                parameterType,
                type,
                fieldInfo.Name);

            return new OverloadedAndPrefixMethodsBuilder(type, propertyName)
                .AsOptionalResult<IOverloadedAndPrefixMethodsBuilder>();
        }

        private class OverloadedAndPrefixMethodsBuilder : IOverloadedAndPrefixMethodsBuilder
        {
            private readonly Type type;
            private readonly string propertyName;

            public OverloadedAndPrefixMethodsBuilder(Type type, string propertyName)
            {
                this.type = type;
                this.propertyName = propertyName;
            }

            public void BuildMethodsWithPrefix(StringBuilder methodsBuilder, string prefix, Type holderType)
            {
                methodsBuilder.AppendFormat(
                    @"        public static IElement<{2}> {0}_{1}(this IElement<{2}> it)
        {{
            return it.{0}({3}.{1});
        }}
",
                    propertyName, prefix, type, holderType);
            }

            public void BuildOverloadedMethods(Type parameterType, StringBuilder stringBuilder, string signatureParametersString, string invokeParametersString)
            {
                stringBuilder.AppendFormat(
                    @"        public static IElement<{2}> {0}(this IElement<{2}> it, {1})
        {{
            return it.{0}(new {3}({4}));
        }}
",
                    propertyName,
                    signatureParametersString,
                    type,
                    InternalUtil.GetCSharTypeName(parameterType),
                    invokeParametersString);
            }
        }
    }
}